//
//  BaseWKWebVC.swift
//  CooperSwift
//
//  Created by 计海峰 on 2020/3/5.
//  Copyright © 2020 CooperSwift. All rights reserved.
//

import UIKit
import WebKit
import SnapKit
import DZNEmptyDataSet

open class BaseWKWebVC: UIViewController {
    
    /// 是否自定义标题
    public var titleStr: String?
    
    // 网址来源
    /// url
    var urlStr: String?
    public var params: [String: Any] = [:]
    /// 文件名字
    public var localHtmlFileName: String?
    /// 片段
    public var localHtmlStr: String?
    public var localHtmlBaseURL: URL?
    
    public var request: URLRequest!
    
    // js 交互
    public var dealJS: Bool = false
    /// 禁止捏合手势
    public var forbidPinches: Bool = true
    
    /// 获取网页内的所有图片
    public var showImages: Bool = true
    public var imageUrls: [String]?
    
    /// post 提交
    public var isPostSumbit: Bool = false
    
    /// 显示空数据
    public var showEmptyData: Bool = false
    
    /// 网页加载是否成功
    public var failedLoading: Bool = false
    
    /// 网址来源
    public var showSourceHint: Bool = false
    lazy var sourceHintLB: UILabel = {
        var hintLB = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: Screen.width, height: 44))
        self.webview.scrollView.insertSubview(hintLB, at: 0)
        hintLB.alpha = 0;
        hintLB.textAlignment = .center
        hintLB.font = .systemFont(ofSize: 13)
        hintLB.textColor = .init(hex: "#C9C9C9")
        hintLB.numberOfLines = 0
        return hintLB
    }()
    
    /// 加载进程
    public var showProgress: Bool = false
    public  var progressTintColor: UIColor = UIColor.red
    public var progressTrackTintColor: UIColor = UIColor.white
    lazy var progressView: UIProgressView = {
        var progress = UIProgressView.init()
        progress.tintColor = progressTintColor
        progress.trackTintColor = progressTrackTintColor
        webview.addSubview(progress)
        progress.snp.makeConstraints { (make) in
            make.top.left.right.equalToSuperview().offset(0)
            make.height.equalTo(1)
        }
        return progress
    }()
    
    /// 是否显示toolbar
    public var showToolbar: Bool = true
    public var toolBarHeight: CGFloat = 50.0
    
    lazy var toolbar: WKWebBottomToolBar = {
        var toolBar = WKWebBottomToolBar.init(frame: CGRect(x: 0, y: 0, width: Screen.width, height: toolBarHeight))
        toolBar.backBT.isEnabled = false
        toolBar.forwardBT.isEnabled = false
        self.view.addSubview(toolBar)
        self.webview.snp.remakeConstraints { (make) in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 0, bottom: toolBarHeight + Screen.bottomBarHeight, right: 0))
        }
        toolBar.snp.makeConstraints { (make) in
            make.left.right.equalToSuperview()
            make.bottom.equalToSuperview().offset(-Screen.bottomBarHeight)
            make.height.equalTo(toolBarHeight)
        }
        return toolBar
    }()
    
    /// wkwebview
    lazy var webview: WKWebView = {
        var config = WKWebViewConfiguration.init() // wkwebview 配置对象
        config.preferences = WKPreferences.init() // 偏好配置
        config.preferences.minimumFontSize = 10 // 默认最小字体
        // 不通过用户交互，是否可以打开窗口
        config.preferences.javaScriptCanOpenWindowsAutomatically = false
        var noneSelectScript: WKUserScript?
        if (dealJS) {
            var javascript = ""
            javascript += "document.documentElement.style.webkitTouchCallout='none';" // 禁止长按
            javascript += "document.documentElement.style.webkitUserSelect='none';" // 禁止选择
            noneSelectScript = WKUserScript.init(source: javascript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        }
        //        else {
        //            config.preferences.javaEnabled = false // iPhone 不能使用
        //        }
        var webview = WKWebView.init(frame: UIScreen.main.bounds, configuration: config)
        if (noneSelectScript != nil) {
            webview.configuration.userContentController.addUserScript(noneSelectScript!)
        }
        webview.uiDelegate = self
        webview.navigationDelegate = self
        webview.scrollView.isScrollEnabled = true
        webview.scrollView.delegate = self
        if showProgress {
            webview.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        }
        if titleStr != nil {
            webview.addObserver(self, forKeyPath: "title", options: .new, context: nil)
        }
        view.addSubview(webview)
        webview.snp.makeConstraints { (make) in
            make.edges.equalToSuperview().inset(UIEdgeInsets.zero)
        }
        return webview
    }()
    
    //MARK: - 初始化
    public convenience init(urlStr: String) {
        self.init()
        self.urlStr = urlStr
    }
    
    public convenience init(localHtmlStr: String, localHtmlBaseURL: URL?) {
        self.init()
        self.localHtmlStr = localHtmlStr
        self.localHtmlBaseURL = localHtmlBaseURL
    }
    
    public convenience init(localHtmlFileName: String) {
        self.init()
        self.localHtmlFileName = localHtmlFileName
    }
    
    open override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        if showToolbar {
            toolbar.backBT.addTarget(self, action: #selector(back), for: .touchUpInside)
            toolbar.forwardBT.addTarget(self, action: #selector(forward), for: .touchUpInside)
            toolbar.centerBT.addTarget(self, action: #selector(reload), for: .touchUpInside)
            webview.addObserver(self, forKeyPath: "canGoBack", options: .new, context: nil)
            webview.addObserver(self, forKeyPath: "canGoForward", options: .new, context: nil)
        }
        loadData()
    }
    
    @objc func back() {
        if webview.canGoBack {
            webview.goBack()
        }
    }
    
    @objc func forward() {
        if webview.canGoForward {
            webview.goForward()
        }
    }
    
    @objc func reload() {
        if showProgress {
            progressView.isHidden = false
        }
        if (localHtmlStr != nil) {
            webview.loadHTMLString(localHtmlStr!, baseURL: localHtmlBaseURL)
            return;
        }
        webview.load(request)
    }
    
    func loadData() {
        if (localHtmlStr != nil) {
            webview.loadHTMLString(localHtmlStr!, baseURL: localHtmlBaseURL)
            return;
        }
        var requestURL: URL?
        if (localHtmlFileName != nil) {
            let strArray = localHtmlFileName?.components(separatedBy: ".")
            var htmlType = "html"
            if strArray?.count == 2 {
                localHtmlFileName = strArray?.first
                htmlType = strArray?.last ?? "html"
            }
            let filePath = Bundle.main.path(forResource: localHtmlFileName, ofType: htmlType)
            if let htmlPath = filePath {
                requestURL = URL.init(fileURLWithPath: htmlPath)
            }
        }else {
            guard let urlString = urlStr else { return }
            guard let url = urlString.spellUrlAndParams(params: params) else { return }
            requestURL = URL.init(string: url)
        }
        guard let URL = requestURL else { return }
        request = URLRequest.init(url: URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 30.0)
        if isPostSumbit {
            request?.httpMethod = "POST"
        }
        
        if showEmptyData {
            webview.scrollView.emptyDataSetSource = self
            webview.scrollView.emptyDataSetDelegate = self
        }
        
        if showSourceHint {
            sourceHintLB.text = "此网页由 \(request.url?.host ?? "") 提供"
        }
        
        webview.load(request)
        
    }
    
}

// MARK: - WKNavigationDelegate
extension BaseWKWebVC: WKNavigationDelegate {
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        webView.evaluateJavaScript("document.documentElement.style.webkitTouchCallout='none';", completionHandler: nil)
        webView.evaluateJavaScript("document.documentElement.style.webkitUserSelect='none';", completionHandler: nil)
        if forbidPinches {
            let injectionJSString = """
                "var script = document.createElement('meta');"
                "script.name = 'viewport';"
                "script.content=\"width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\";"
                "document.getElementsByTagName('head')[0].appendChild(script);"
            """
            webView.evaluateJavaScript(injectionJSString, completionHandler: nil)
        }
        if failedLoading {
            if showEmptyData {
                webView.scrollView.reloadEmptyDataSet()
            }
            failedLoading = false
        }else {
            title = titleStr ?? webView.title
        }
        if showImages {
            webImageUrlsByJS()
        }
    }
    
    public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        failedLoading = true
        if showEmptyData {
            webView.scrollView.reloadEmptyDataSet()
        }
        title = ""
    }
    
    public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        decisionHandler(.allow)
    }
}


class WeakScriptMessageDelegate:NSObject, WKScriptMessageHandler {
    
    weak var delegate: WKScriptMessageHandler?
    
    init(_ delegate: WKScriptMessageHandler) {
        super.init()
        self.delegate = delegate
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        self.delegate?.userContentController(userContentController, didReceive: message)
    }
    
}

extension BaseWKWebVC: WKScriptMessageHandler  {
    private struct AssociatedKey {
        static var key = "cp_methods"
    }
    var methods:Set<String> {
        set {
            objc_setAssociatedObject(self, &AssociatedKey.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get {
            return objc_getAssociatedObject(self, &AssociatedKey.key) as? Set<String> ?? Set<String>()
        }
    }
    
    /// js 执行 siwft 方法  方法名前 必须是 @objc 参数名前面必须是_
    /// - Parameter method: 注册方法名
    public func addSwiftMethod(method: String) {
        methods.insert(method)
        guard methods.contains(method) else {
            return
        }
        self.webview.configuration.userContentController.add(WeakScriptMessageDelegate.init(self), name: method)
    }
    
    
    /// 执行js方法
    /// - Parameter method: <#method description#>
    public func doJSMethod(method: String) {
        webview.evaluateJavaScript(method) { (info, error) in
            
        }
    }
    
    public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        let method : String? = message.name
        guard method?.count ?? 0 > 0 else { return }
        let sel = Selector("\(method!):")
        if responds(to: sel) {
            perform(sel, with: message.body)
        }
    }
    
}


// userAgent
extension BaseWKWebVC {
    
    static func addGlobalUserAgent(agent: String) {
        WKWebView().evaluateJavaScript("navigator.userAgent") {(info, error) in
            if var userAgent = info as? String {
                if userAgent.hasSuffix(agent) == false {
                    userAgent += agent
                }
                // 设置global User-Agent
                let dic = ["UserAgent": userAgent]
                UserDefaults.standard.register(defaults: dic)
                UserDefaults.standard.synchronize()
            }
            
        }
    }
    
    func addUserAgent(agent: String) {
        webview.evaluateJavaScript("navigator.userAgent") { [weak webview] (info, error) in
            if var userAgent = info as? String {
                if userAgent.hasSuffix(agent) == false {
                    userAgent += agent
                }
                webview?.customUserAgent = userAgent
                webview?.reload()
            }
            
        }
    }
}

// MARK: - WKNavigationDelegate
extension BaseWKWebVC: WKUIDelegate {
    public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        let alert = UIAlertController.init(title: titleStr ?? "提示", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确认", style: .default) { (action) in
            completionHandler()
        })
        present(alert, animated: true, completion: nil)
    }
    
    public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        let alert = UIAlertController.init(title: titleStr ?? "提示", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "取消", style: .cancel) { (action) in
            completionHandler(false)
        })
        alert.addAction(UIAlertAction(title: "确认", style: .default) { (action) in
            completionHandler(true)
        })
        present(alert, animated: true, completion: nil)
    }
    
    public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        let alert = UIAlertController.init(title: prompt, message: "", preferredStyle: .alert)
        alert.addTextField { (textField) in
            textField.text = defaultText
        }
        alert.addAction(UIAlertAction(title: "完成", style: .default) { (action) in
            completionHandler(alert.textFields?[0].text ?? "")
        })
        present(alert, animated: true, completion: nil)
    }
}


// MARK: - scrollView delegate
extension BaseWKWebVC: UIScrollViewDelegate {
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //        if showToolbar {
        //
        //        }
        if !showSourceHint { return }
        if scrollView.contentOffset.y < -40 {
            if scrollView.contentOffset.y > -60 {
                sourceHintLB.alpha = (-scrollView.contentOffset.y - 40)/20
            }
        }else {
            sourceHintLB.alpha = 0
        }
        sourceHintLB.y = scrollView.contentOffset.y
    }
}

// MARK: - observe
extension BaseWKWebVC {
    open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if (webview.isEqual(object)) {
            if keyPath == "estimatedProgress" {
                let progress = (change?[.newKey] as! NSNumber).floatValue
                progressView.progress = progress
                if progress >= 1 {
                    progressView.isHidden = true
                }
            }else
                if keyPath == "canGoBack" {
                    toolbar.backBT.isEnabled = (change?[.newKey] as! NSNumber).boolValue
                }else
                    if keyPath == "canGoForward" {
                        toolbar.forwardBT.isEnabled = (change?[.newKey] as! NSNumber).boolValue
                    }else
                        if keyPath == "title" {
                            title = titleStr ?? webview.title
            }
        }else{
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
}

// MARK: - DZNEmptyDataSetSource
//extension BaseWKWebVC: DZNEmptyDataSetSource, DZNEmptyDataSetDelegate {
//    private func description(forEmptyDataSet scrollView: UIScrollView) -> NSAttributedString? {
//        let paragraph = NSMutableParagraphStyle()
//        paragraph.lineBreakMode = .byWordWrapping
//        paragraph.lineSpacing = 2.0
//        paragraph.alignment = .center
//        let attributes = [NSAttributedString.Key.foregroundColor.rawValue: UIColor(red: 125/255.0, green: 127/255.0, blue: 127/255.0, alpha: 1.0),
//                          NSAttributedString.Key.paragraphStyle: paragraph,
//                          NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14)] as [AnyHashable : NSObject]
//            as [AnyHashable : NSObject]
//        return NSAttributedString.init(string: "网络连接错误,点此刷新", attributes: attributes as? [NSAttributedString.Key : Any])
//    }
//    
//    private func emptyDataSet(_ scrollView: UIScrollView, didTap view: UIView) {
//        self.reload()
//    }
//}

// MARK: - 获取文本webview里面的图片
extension BaseWKWebVC {
    
    func webImageUrlsByJS() {
        let jsForImages = """
            function getImages(){
            var objs = document.getElementsByTagName("img");
            var imgScr = '';
            for(var i=0;i<objs.length;i++){
            imgScr = imgScr + objs[i].src + '[&&&]';
            };
            return imgScr;
            };
        """
        webview.evaluateJavaScript(jsForImages, completionHandler: nil)
        
        let method = "getImages()"
        webview.evaluateJavaScript(method) { [weak self] (result, error) in
            var imagesStr = String(describing: result)
            if imagesStr.hasPrefix("[&&&]") {
                imagesStr = imagesStr.substring(from: 5)
            }
            self?.imageUrls = imagesStr.components(separatedBy: "[&&&]")
        }
    }
    
}
